package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.factory.DeliveryStrategyFactory;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.atguigu.tingshu.vo.user.UserSubscribeVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private UserSubscribeMapper userSubscribeMapper;

    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    @Autowired
    private DeliveryStrategyFactory deliveryStrategyFactory;




    @Override
    public Map<String, String> wxLoginGetToken(String code) {

        try {
            //1.拿着临时凭据+应用ID+应用秘钥 调用微信接口 获取当前微信账户唯一标识：openId
            //1.1 微信账户信息业务类
            WxMaUserService userService = wxMaService.getUserService();

            //1.2 获取会话信息
            WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);

            //1.3 获取微信账号唯一标识
            String openid = sessionInfo.getOpenid();
            //2.根据微信账户唯一标识，查询数据库，看当前微信是否已经注册

            UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>()
                    .eq(UserInfo::getWxOpenId, openid));
            //3.如果微信账户首次注册，则新增用户记录，为用户初始化账户记录用于后续订单支付
            if (userInfo == null) {
                //3.1 新增用户记录 绑定微信账户唯一标识
                userInfo = new UserInfo();
                userInfo.setWxOpenId(openid);
                userInfo.setNickname(IdUtil.nanoId());
                userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2025-07-21/6128c262-0c51-494f-833f-d9dc59cbdb24.png");

                userInfoMapper.insert(userInfo);
                //3.2
                // 方案一：Openfeign远程调用 分布式事务问题  方案二：采用MQ可靠性消息队列实现数据最终一致

                //采用消息队列的模式
                //3.2.1 构建消息对象 注意：如果是VO对象一定要实现序列化接口以及生成序列化版本号
                Map<String, Object> map = new HashMap<>();
                map.put("userId", userInfo.getId());
                map.put("amount", new BigDecimal("100"));
                map.put("title", "新用户专项体验金活动");
                map.put("orderNo", "ZS" + IdUtil.getSnowflakeNextId());

                //3.2.2 发送消息到MQ
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, map);
            }

            //4.基于当前用户信息生成令牌
            //4.1 创建令牌
            String token = IdUtil.randomUUID().replace("-", "").toString();

            //4.2 构建登录成功后Redis的Key 形式为：user:login:token
            String key = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //4.3 构建登录成功后Redis的Value 形式为：userInfoVo
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);

            //4.4 存入Redis 设置有效期：14天
            redisTemplate.opsForValue().set(key, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);

            //5.封装令牌返回给前端
            HashMap<String, String> hashMap = new HashMap<>();

            hashMap.put("token", token);


            return hashMap;
        } catch (WxErrorException e) {
            log.error("微信登录失败");
            throw new GuiguException(500, "微信登录失败");
        }
    }

    @Override
    @GuiGuCache(prefix = "user:info:")
    public UserInfoVo getUserInfoVo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);

        UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
        return userInfoVo;
    }

    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStatusTrackIdList) {

        Map<Long, Integer> map = new HashMap<>();
        //1.查询用户买没买过专辑,如果买过专辑直接设置本页声音为已购买
        List<UserPaidAlbum> userPaidAlbumList = userPaidAlbumMapper.selectList(new LambdaQueryWrapper<UserPaidAlbum>()
                .eq(UserPaidAlbum::getUserId, userId)
                .eq(UserPaidAlbum::getAlbumId, albumId)
        );

        if (CollUtil.isNotEmpty(userPaidAlbumList)) {
            //走到这说明买过专辑,将本页的音乐都设置为已购买
            for (Long id : needCheckPayStatusTrackIdList) {
                map.put(id, 1);
            }
            return map;
        }


        //2.走到这说明每购买过专辑,这时候又分两种情况:查询已购声音表,看这个用户在此专辑下购没购买过单个的某个音乐


        List<UserPaidTrack> userPaidTracks = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>()
                .eq(UserPaidTrack::getUserId, userId)
                .eq(UserPaidTrack::getAlbumId, albumId)
                .select(UserPaidTrack::getTrackId)
        );

        List<Long> userPaidTrackIdList = userPaidTracks.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());

        if (CollUtil.isEmpty(userPaidTrackIdList)) {
            //2.1 第一种情况 ,没购买过,将需要检查的声音列表全设为0返回
            for (Long id : needCheckPayStatusTrackIdList) {
                map.put(id, 0);
            }
            return map;
        }

        //2.2 第二种情况 ,购买过,需要单独将购买的声音设置为1,其他为0
        //区分那个买过哪个没买过
        for (Long id : needCheckPayStatusTrackIdList) {
            if (userPaidTrackIdList.contains(id)) {
                map.put(id, 1);
            } else {
                map.put(id,0);
            }
        }
        return map;
    }



    /**
     * 用户订阅接口
     *
     * @param albumId
     * @param userId
     * @return
     */
    @Override
    public Boolean subscribe(Long albumId, Long userId) {

        LambdaQueryWrapper<UserSubscribe> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(UserSubscribe::getUserId,userId);
        wrapper.eq(UserSubscribe::getAlbumId,albumId);

        UserSubscribe oldUserSubscribe = userSubscribeMapper.selectOne(wrapper);

        Boolean result = true;

        if (oldUserSubscribe != null){
            userSubscribeMapper.deleteById(oldUserSubscribe);
            result = false;
            return result;
        }

        UserSubscribe userSubscribe = new UserSubscribe();

        userSubscribe.setUserId(userId);
        userSubscribe.setAlbumId(albumId);

        userSubscribeMapper.insert(userSubscribe);
        return result;

    }

    /**
     * 判断用户是否购买过指定专辑
     *
     * @param albumId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long albumId, Long userId) {

        UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>()
                .eq(UserPaidAlbum::getAlbumId, albumId)
                .eq(UserPaidAlbum::getUserId, userId)
        );

        if (userPaidAlbum != null){
            return true;
        }else {
            return false;
        }

    }


    /**
     * 查询指定用户指定专辑下已购声音ID列表
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
                new LambdaQueryWrapper<UserPaidTrack>()
                        .eq(UserPaidTrack::getUserId, userId)
                        .eq(UserPaidTrack::getAlbumId, albumId)
                        .select(UserPaidTrack::getTrackId)
        );
        if (CollUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }


    /**
     * 新增用户购买记录（声音、专辑、VIP）____虚拟物品发货接口
     * @param userPaidRecordVo
     * @return
     */
    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {

        DeliveryStrategy deliveryStrategy = deliveryStrategyFactory.getDeliveryStrategy(userPaidRecordVo.getItemType());

        deliveryStrategy.delivery(userPaidRecordVo);
      /*  //根据type来判断用户买的是那种商品,是VIP还是专辑,还是声音

        Long userId = userPaidRecordVo.getUserId();

        String itemType = userPaidRecordVo.getItemType();

        String orderNo = userPaidRecordVo.getOrderNo();

        List<Long> itemIdList = userPaidRecordVo.getItemIdList();

        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)){
            //走到这说明买的是专辑
            //判断是否购买过专辑,购买过直接忽略
            Long count = userPaidAlbumMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidAlbum>()
                            .eq(UserPaidAlbum::getOrderNo, orderNo)
                            .select(UserPaidAlbum::getId)
            );

            if (count > 0){
                return;
            }

            //走到这说明没处理过
            //保存声音购买记录
            UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
            userPaidAlbum.setUserId(userId);
            userPaidAlbum.setOrderNo(orderNo);
            userPaidAlbum.setAlbumId(itemIdList.get(0));
            userPaidAlbumMapper.insert(userPaidAlbum);


        }else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)){
            //走到这说明买的是声音
            //判断是否购买过声音

            TrackInfo trackInfo = albumFeignClient.getTrackInfo(itemIdList.get(0)).getData();
            Long albumId = trackInfo.getAlbumId();
            Long count = userPaidTrackMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidTrack>()
                            .eq(UserPaidTrack::getOrderNo, orderNo)
                            .select(UserPaidTrack::getId)
            );

            if (count > 0){
                return;
            }

            //走到这说明没处理过订单,保存订单
            //遍历id集合,批量导入
            for (Long trackId : itemIdList) {
                UserPaidTrack userPaidTrack = new UserPaidTrack();
                userPaidTrack.setUserId(userId);
                userPaidTrack.setOrderNo(orderNo);
                userPaidTrack.setAlbumId(albumId);
                userPaidTrack.setTrackId(trackId);

                userPaidTrackMapper.insert(userPaidTrack);
            }


        }else if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)){
            //走到这说明买的是VIP
            //这个功能比较复杂
            //1.首先需要根据订单编号查询是否处理过订单
            Long count = userVipServiceMapper.selectCount(
                    new LambdaQueryWrapper<UserVipService>()
                            .eq(UserVipService::getOrderNo, orderNo)
                            .select(UserVipService::getId)
            );

            if (count>0){
                return;
            }

            //2.然后需要新增会员购买记录
            UserVipService userVipService = new UserVipService();
            userVipService.setUserId(userId);
            userVipService.setOrderNo(orderNo);

            //获取用户身份信息,判断是否为VIP
            UserInfoVo userInfoVo = this.getUserInfoVo(userId);

            Integer voIsVip = userInfoVo.getIsVip();

            Date vipExpireTime = userInfoVo.getVipExpireTime();

            boolean isVip = false;
            if (voIsVip == 1 && vipExpireTime.after(new Date())){
                //是VIP
                isVip = true;
            }


            //获取用户购买的套餐信息,得到套餐服务
            Long vipId = itemIdList.get(0);

            VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(vipId);

            Integer serviceMonth = vipServiceConfig.getServiceMonth();

            if (!isVip){
                //普通用户
                //生效时间为当前时间
                userVipService.setStartTime(new Date());

                //到期时间
                userVipService.setExpireTime(DateUtil.offsetMonth(new Date(),serviceMonth));

            }else {
                //会员VIP
                //本次会员的生效时间为会员到期时间+1天
                DateTime dateTime = DateUtil.offsetDay(userInfoVo.getVipExpireTime(), 1);
                userVipService.setStartTime(dateTime);

                //本次会员的到期时间
                DateTime offsetMonth = DateUtil.offsetMonth(userVipService.getStartTime(), serviceMonth);
                userVipService.setExpireTime(offsetMonth);
            }

            userVipServiceMapper.insert(userVipService);

            //3更新用户会员标识以及更新会员过期时间
            userInfoVo.setIsVip(1);
            userInfoVo.setVipExpireTime(userVipService.getExpireTime());
            UserInfo userInfo = BeanUtil.copyProperties(userInfoVo, UserInfo.class);
            userInfoMapper.updateById(userInfo);

        }
*/

    }


    /**
     * 获取用户是否订阅
     * @param albumId
     * @param userId
     * @return
     */
    @Override
    public Boolean isSubscribe(Long albumId, Long userId) {
        Long count = userSubscribeMapper.selectCount(
                new LambdaQueryWrapper<UserSubscribe>()
                        .eq(UserSubscribe::getUserId, userId)
                        .eq(UserSubscribe::getAlbumId, albumId)
        );

        Boolean result = false;

        if (count > 0){
            result = true;
        }

        return result;
    }


    @Override
    public Page<UserSubscribeVo> findUserSubscribePage(Page<UserSubscribeVo> page1, Long userId) {

        //先获取用户订阅的专辑集合
        List<UserSubscribe> userSubscribes = userSubscribeMapper.selectList(
                new LambdaQueryWrapper<UserSubscribe>()
                        .eq(UserSubscribe::getUserId, userId)
                        .select(UserSubscribe::getAlbumId)
        );


        List<Long> albumIdList = userSubscribes.stream()
                .map(e -> e.getAlbumId()).collect(Collectors.toList());


        Map<Long, AlbumInfo> map = albumIdList.stream()
                .map(id -> albumFeignClient.getAlbumInfo(id).getData())
                .collect(Collectors.toMap(AlbumInfo::getId, albumInfo -> albumInfo));

        Assert.notNull(map,"专辑不存在");

        page1 =  userSubscribeMapper.findUserSubscribePage(page1,userId);

        page1.getRecords().stream()
                .forEach(vo -> {
                    Long albumId = vo.getAlbumId();
                    if (map.containsKey(albumId)){
                        vo.setCoverUrl(map.get(albumId).getCoverUrl());
                        vo.setAlbumTitle(map.get(albumId).getAlbumTitle());
                        vo.setIncludeTrackCount(map.get(albumId).getIncludeTrackCount());
                        vo.setIsFinished(map.get(albumId).getIsFinished());
                        vo.setCreateTime(map.get(albumId).getCreateTime());
                    }
                });

        return page1;
    }


}
